#pragma once
#include "resource.h"
#include "DotNetProfiler.h"
#include "CorProfilerCallbackImpl.h"
#include "resource.h"
#include "FunctionInfo.h"
#include <map>

#if defined(_WIN32_WCE) && !defined(_CE_DCOM) && !defined(_CE_ALLOW_SINGLE_THREADED_OBJECTS_IN_MTA)
#error "Single-threaded COM objects are not properly supported on Windows CE platform, such as the Windows Mobile platforms that do not include full DCOM support. Define _CE_ALLOW_SINGLE_THREADED_OBJECTS_IN_MTA to force ATL to support creating single-thread COM object's and allow use of it's single-threaded COM object implementations. The threading model in your rgs file was set to 'Free' as that is the only threading model supported in non DCOM Windows CE platforms."
#endif

#define ASSERT_HR(x) _ASSERT(SUCCEEDED(x))
#define NAME_BUFFER_SIZE 1024

#define ENVIRONMENT_MAX_BUFFER_SIZE 1024



#define PROFILER_APPLICATION_NAME TEXT("PROFILER_APPLICATION_NAME")

#define PROFILER_APPLICATION_PATH TEXT("PROFILER_APPLICATION_PATH")


//outuput files settings
#define NUMFILES	10
#define MAXLINES	20000

//#define VERBOSE_MODE

// CProfiler
class ATL_NO_VTABLE CProfiler :
	public CComObjectRootEx<CComSingleThreadModel>,
	public CComCoClass<CProfiler, &CLSID_Profiler>,
	public CCorProfilerCallbackImpl
{
public:
	CProfiler();
	~CProfiler();

	DECLARE_REGISTRY_RESOURCEID(IDR_PROFILER)
	BEGIN_COM_MAP(CProfiler)
		COM_INTERFACE_ENTRY(ICorProfilerCallback)
		COM_INTERFACE_ENTRY(ICorProfilerCallback2)
	END_COM_MAP()
	DECLARE_PROTECT_FINAL_CONSTRUCT()

	// overridden implementations of FinalConstruct and FinalRelease
	HRESULT FinalConstruct();
	void FinalRelease();

	// STARTUP/SHUTDOWN EVENTS
	STDMETHOD(Initialize)(IUnknown *pICorProfilerInfoUnk);
	STDMETHOD(Shutdown)();

	// THREAD RELATED EVENTS
	STDMETHOD(ThreadCreated)(ThreadID threadID);
	STDMETHOD(ThreadDestroyed)(ThreadID threadID);

	//EXCEPTION RELATED EVENTS
	STDMETHOD(ExceptionCatcherEnter)(FunctionID functionId, ObjectID objectId);
	STDMETHOD(ExceptionCatcherLeave)();
	STDMETHOD(ExceptionThrown)(ObjectID thrownObjectId);

	//OBJECTS ALLOCATION RELATED EVENTS
	STDMETHOD(ObjectAllocated)(ObjectID objectId, ClassID classID);

	//GARBAGE COLLECTION RELATED EVENTS
	STDMETHOD(GarbageCollectionStarted)(int cGenerations, BOOL generationCollected[], COR_PRF_GC_REASON reason);
	STDMETHOD(GarbageCollectionFinished)();
	STDMETHOD(FinalizeableObjectQueued)(DWORD finalizerFlags,ObjectID objectID);

	// callback functions
	void Enter(FunctionID functionID, UINT_PTR clientData, COR_PRF_FRAME_INFO frameInfo, COR_PRF_FUNCTION_ARGUMENT_INFO *argumentInfo);
	void Leave(FunctionID functionID, UINT_PTR clientData, COR_PRF_FRAME_INFO frameInfo, COR_PRF_FUNCTION_ARGUMENT_RANGE *argumentRange);
	void Tailcall(FunctionID functionID, UINT_PTR clientData, COR_PRF_FRAME_INFO frameInfo);

	// mapping functions
	static UINT_PTR _stdcall FunctionMapper(FunctionID functionId, BOOL *pbHookFunction);
	void MapFunction(FunctionID);

	// logging function
	void LogString(char* pszFmtString, ... );

private:
	LPTSTR			pszApplicationName;
	LPTSTR			pszApplicationPath;

	CRITICAL_SECTION criticalSection;
	CRITICAL_SECTION criticalSectionDBwrite;

	// container for ICorProfilerInfo reference
	CComQIPtr<ICorProfilerInfo> m_pICorProfilerInfo;
	// container for ICorProfilerInfo2 reference
	CComQIPtr<ICorProfilerInfo2> m_pICorProfilerInfo2;
	// STL map for our hashed functions
	std::map<FunctionID, CFunctionInfo*> m_functionMap;
	// STL map for application thread, for each thread contains it's stack call count (depth of stack)
	std::map<long,int> m_ThreadMap;
	// the number of levels deep we are in the call stack
	int m_callStackSize;

	//the number of thread created
	int m_threadNumber;

	// handle and filename of log file
	HANDLE m_hLogFile[NUMFILES];
	HANDLE _fakeLog;
	TCHAR m_logFileName[_MAX_PATH]; 
	int m_lines; //Lines in current log file
	int m_fileNumber; //Number of current file

	//Get info from the function, exclude is set to true if the function has to be excluded
	HRESULT GetMethodInfo(CFunctionInfo *functionInfo);

	// function to set up our event mask
	HRESULT SetEventMask();
	// creates the log file
	void CreateLogFile();
	// closes the log file ;)
	void CloseLogFile();
	//Load settings from environment variables
	void LoadSettings();

	//Timing helpers
	double start;
	double finish;
	double freq;
	void GetTimestamp(char *buffer);

	void LogEvent(	int		eventType, 
					long	idThread, 
					WCHAR*	nameThread, 
					long	idFunction, 
					WCHAR*	nameFunction,
					long	idClass,
					long	idObject,
					long	stackDepth,
					char*	timeBuffer,
					WCHAR*	exceptionName,
					bool	isStatic);

	//Critical section, to avoid simultaneous writing to log file on multi-thread programs
};

OBJECT_ENTRY_AUTO(__uuidof(Profiler), CProfiler)
